﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace XChipUtils
{

   
    public class Lagrange : ILagrangeHelper
    {
        public int[] x { get; set; }
        public double[] y { get; set; }
        public int itemNum { get; set; }

        public Lagrange()
        {
        }

        public Lagrange(int[] x, double[] y)
        {
            this.x = x;
            this.y = y;
            this.itemNum = x.Length;
        }

        public double GetValue(int xValue)
        {
            int start, end;
            double value = 0.0;

            if (itemNum < 1) { return value; }
            if (itemNum == 1) { return y[0]; }
            if (itemNum == 2)
            {
                value = (y[0] * (xValue - x[1]) - y[1] * (xValue - x[0])) / (x[0] - x[1]);
                return value;
            }

            if (xValue <= x[1]) { start = 0; end = 2; }
            else if (xValue >= x[itemNum - 2]) { start = itemNum - 3; end = itemNum - 1; }
            else
            {
                start = 1; end = itemNum;
                int temp;
                while ((end - start) != 1)
                {
                    temp = (start + end) / 2;
                    if (xValue < x[temp - 1])
                        end = temp;
                    else
                        start = temp;
                }
                start--; end--;
                if (Math.Abs(xValue - x[start]) < Math.Abs(xValue - x[end]))
                    start--;
                else
                    end++;
            }

            double valueTemp;
            for (int i = start; i <= end; i++)
            {
                valueTemp = 1.0;
                for (int j = start; j <= end; j++)
                    if (j != i)
                        valueTemp *= (double)(xValue - x[j]) / (double)(x[i] - x[j]);
                value += valueTemp * y[i];
            }
            return value;
        }

        public double[] CalcCurve()
        {
            double[] output_y = new double[256];
            if (x.Length < 2)
            {
                for (int i = 0; i < 256; i++)
                {
                    output_y[i] = i;
                }
            }
            else if (x.Length == 2)
            {
                double delty_y = 0;
                if (x.First() != x.Last())
                {
                    delty_y = (y.Last() - y.First()) * 1.0 / (x.Last() - x.First());
                }
                for (int i = 0; i < 256; i++)
                {
                    if (i < x.First())
                    {
                        output_y[i] = y.First();
                    }
                    else if (i >= x.First() && i < x.Last())
                    {
                        output_y[i] = CLIP_RANGE((y.First() + delty_y * (i - x.First())), 0, 255);
                    }
                    else
                    {
                        output_y[i] = y.Last();
                    }
                }
            }
            else
            {
                // 其他情况的插值逻辑
            }
            return output_y;
        }

        public  double CLIP_RANGE(double value, int min, int max)
        {
            return (value) > (max) ? (max) : (((value) < (min)) ? (min) : (value));
        }

        public double[] Spline()
        {
            //初始化最大数组值
            var m = x.Last() - x.First();
            var n = x.Count();
            //返回值
            m++;
            double[] t = new double[m];
            double[] z = new double[m];
            for (int i = 0; i < m; i++)
            {
                t[i] = i;
            }

            double[] dy = new double[n];
            dy[0] = -0.5;
            double[] ddy = new double[n];

            double h1 = 0;
            double[] s = new double[n];
            double h0 = x[1] - x[0];
            s[0] = 3.0 * (y[1] - y[0]) / (2.0 * h0) - ddy[0] * h0 / 4.0;
            for (int j = 1; j <= n - 2; ++j)
            {
                h1 = x[j + 1] - x[j];
                double alpha = h0 / (h0 + h1);
                double beta = (1.0 - alpha) * (y[j] - y[j - 1]) / h0;
                beta = 3.0 * (beta + alpha * (y[j + 1] - y[j]) / h1);
                dy[j] = -alpha / (2.0 + (1.0 - alpha) * dy[j - 1]);
                s[j] = (beta - (1.0 - alpha) * s[j - 1]);
                s[j] = s[j] / (2.0 + (1.0 - alpha) * dy[j - 1]);
                h0 = h1;
            }
            dy[n - 1] = (3.0 * (y[n - 1] - y[n - 2]) / h1 + ddy[n - 1] * h1 / 2.0 - s[n - 2]) / (2.0 + dy[n - 2]);

            for (int j = n - 2; j >= 0; --j)
            {
                dy[j] = dy[j] * dy[j + 1] + s[j];
            }

            for (int j = 0; j <= n - 2; ++j)
            {
                s[j] = x[j + 1] - x[j];
            }

            for (int j = 0; j <= n - 2; ++j)
            {
                h1 = s[j] * s[j];
                ddy[j] = 6.0 * (y[j + 1] - y[j]) / h1 - 2.0 * (2.0 * dy[j] + dy[j + 1]) / s[j];
            }


            h1 = s[n - 2] * s[n - 2];
            ddy[n - 1] = 6.0 * (y[n - 2] - y[n - 1]) / h1 + 2.0 * (2.0 * dy[n - 1] + dy[n - 2]) / s[n - 2];
            double g = 0.0;
            for (int i = 0; i <= n - 2; i++)
            {
                h1 = 0.5 * s[i] * (y[i] + y[i + 1]);
                h1 = h1 - s[i] * s[i] * s[i] * (ddy[i] + ddy[i + 1]) / 24.0;
                g = g + h1;
            }

            for (int j = 0; j <= m - 1; j++)
            {
                int i;
                if (t[j] >= x[n - 1])
                {
                    i = n - 2;
                }
                else
                {
                    i = 0;
                    while (t[j] > x[i + 1])
                    {
                        i = i + 1;
                    }
                }
                h1 = (x[i + 1] - t[j]) / s[i];
                h0 = h1 * h1;
                z[j] = (3.0 * h0 - 2.0 * h0 * h1) * y[i];
                z[j] = z[j] + s[i] * (h0 - h0 * h1) * dy[i];
                h1 = (t[j] - x[i]) / s[i];
                h0 = h1 * h1;
                z[j] = z[j] + (3.0 * h0 - 2.0 * h0 * h1) * y[i + 1];
                z[j] = z[j] - s[i] * (h0 - h0 * h1) * dy[i + 1];
            }

            return z;
        }
    }
}
